seaN aitkeN – The Blog

November 30, 2006

Hashtable serialization and the IDeserializationCallback interface

Filed under: .NET — clevercoder @ 3:09 pm

It turns out that the process of serializing and deserializing certain classes in the .NET Framework isn’t always as straightforward as it seems. Let’s start with the problem. We have a Hashtable that we would want to maintain as part of the serialization of a particular class. Let’s call this BoringClass.

Inside the GetObjectData() method of BoringClass, we try to add a Hashtable to the SerializationInfo object. An example would be:

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
     Hashtable hashtable = new Hashtable();
     hashtable["testItem"];

     info.AddValue("hash", hashtable);
}

The problem is when we try to deserialize the object, such as:

public BoringClass(SerializationInfo info, StreamingContext context)
{
     Hashtable hashtable = (Hashtable) info.GetValue("hash", typeof(Hashtable));

     Console.WriteLine("Value is: " + hashtable["testItem"]);
}

And, subsequently try to acccess said Hashtable, you may get a rather vague runtime exception like:

Exception has been thrown by the target of an invocation.

Inspecting the InnerException gives us a clue:

{"Object reference not set to an instance of an object."}

Warmer…
Setting a breakpoint in the special constructor and inspecting the SerializationInfo object prior to doing anything with it shows us more.. in that the Hashtable is basically broken:

info.m_data
{Dimensions:[4]}
    [0]: Count = 0
    [1]: null
    [2]: null
    [3]: null

The first element is recognized as a Hashtable, but has no elements!

Cutting to the chase, it all boils down to the fact that Hashtable implements the interface IDeserializationCallback. That interface is rather trivial, but the implications are far reaching.

Using Reflector for .NET, I cracked open the hood of the Hashtable class. Look at the special constuctor for serialization. Notice anything funny?

protected Hashtable(SerializationInfo info, StreamingContext context)
{
      this.m_siInfo = info;
}

Comparing this to the serialization constructor on a DataTable:

protected DataTable(SerializationInfo info, StreamingContext context) : this()
{
      bool flag1 = (context.Context != null) ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
      SerializationFormat format1 = SerializationFormat.Xml;
      SerializationInfoEnumerator enumerator1 = info.GetEnumerator();
      while (enumerator1.MoveNext())
      {
            string text1;
            if (((text1 = enumerator1.Name) != null) && (text1 == "DataTable.RemotingFormat"))
            {
                  format1 = (SerializationFormat) enumerator1.Value;
            }
      }
      this.DeserializeDataTable(info, context, flag1, format1);
}

It’s pretty obvious that something is different. Well, looking forther, I found the deserialization code in the OnDeserialization event handler method. So it seems that for the Hashtable to be fully deserialized, the OnDeserialization method must be invoked!

It turns out that a handful of classes in the framework depend on the IDeserializationCallback. (16 in fact). The IDeserializationCallback interface, and the process in general is described in length on this page: http://msdn2.microsoft.com/en-us/library/ms973893.aspx

The meaty part:

“Objects are reconstructed from the inside out, and calling methods during deserialization can have undesirable side effects, since the methods called might refer to object references that have not been deserialized by the time the call is made. If the class being deserialized implements the IDeserializationCallback, the OnSerialization method will automatically be called when the entire object graph has been deserialized. At this point, all the child objects referenced have been fully restored. A hash table is a typical example of a class that is difficult to deserialize without using the event listener described above. It is easy to retrieve the key/value pairs during deserialization, but adding these objects back to the hash table can cause problems since there is no guarantee that classes that derived from the hash table have been deserialized. Calling methods on a hash table at this stage is therefore not advisable.”

So, it seems that the Hashtable will serialize properly when created directly from Formatter.Serialize(…) and Formatter.DeSerialize(…). It seems that the Formatter wires up and calls the OnDeserialization(…) method on the object in question. The problem here is that we are using info.info.GetValue(...) to retrieve the Hashtable object. The SerializationInfo class only calls the special constructor and the OnDeserialization(…) method is never called.

The solution:

Simply call “OnDeserialization(…)” yourself after the object has been retrieved with GetValue! Simple, eh?!

So the modified code would look like:

        public BoringClass(SerializationInfo info, StreamingContext context)
        {
            Hashtable hashtable = (Hashtable) info.GetValue("hash", typeof(Hashtable));
            hashtable.OnDeserialization(this);

            Console.WriteLine("Value is: " + hashtable["testItem"]);

        }

I’m not sure if this can be considered the “best” practice, but it solves the problem for now.

ope someone else can benefit from this.

Cheers!
-Sean

4 Comments »

  1. Hi

    Is there any new way to serialize Hashtable?

    Tx

    Comment by Abdolhosein V. Ebrahimi — August 19, 2008 @ 10:32 am

  2. Thanks for your post!
    I was looking for a solution for a day now…

    Comment by Fang — March 16, 2009 @ 2:20 pm

  3. Thank you for posting this solution. I had finally found that m_sInfo held the data I needed, but I was at a loss as to how to retrieve it.

    You have saved me a great deal of time!

    Regards.

    Comment by DavidB — August 17, 2009 @ 2:21 pm

  4. […] Hashtable serialization and the IDeserializationCallback interface « seaN aitkeN – The Blog. […]

    Pingback by Hashtable serialization and the IDeserializationCallback interface « seaN aitkeN – The Blog | ObondO Labs — May 3, 2012 @ 3:01 am


RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.